﻿using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;

namespace Sencha.Serialization.Serializers
{
	using Contracts;

	public sealed class DictionarySerializer<DictionaryT> : TypeSerializer<DictionaryT>
	{
		private struct CachedSerializationMethods
		{
			public static readonly Action<IJsonWriter, DictionaryT> WriteAll;
			public static readonly Func<IJsonReader, DictionaryT> ReadAll;

			static CachedSerializationMethods()
			{
				var dictionaryType = typeof(DictionaryT);
				var keyT = typeof(object);
				var valueT = typeof(object);
				var isGeneric = false;
				var isDictionaryInterface = dictionaryType == typeof(IDictionary) || (dictionaryType.IsGenericType && dictionaryType.GetGenericTypeDefinition() == typeof(IDictionary<,>));

				if (dictionaryType.IsInstantiationOf(typeof(IDictionary<,>)))
				{
					var genArgs = dictionaryType.GetInstantiationArguments(typeof(IDictionary<,>));
					isGeneric = true;
					keyT = genArgs[0];
					valueT = genArgs[1];
				}

				var readAll = new DynamicMethod("ReadAll<" + dictionaryType.Name + ">", dictionaryType, new[] { typeof(IJsonReader) }, typeof(JsonSerializer).Module, dictionaryType.IsNotPublic || keyT.IsNotPublic || valueT.IsNotPublic);
				readAll.DefineParameter(1, ParameterAttributes.None, "reader");
				readAll.InitLocals = true;

				var writeAll = new DynamicMethod("WriteAll<" + dictionaryType.Name + ">", typeof(void), new[] { typeof(IJsonWriter), dictionaryType }, typeof(JsonSerializer).Module, dictionaryType.IsNotPublic || keyT.IsNotPublic || valueT.IsNotPublic);
				writeAll.DefineParameter(1, ParameterAttributes.None, "writer");
				writeAll.DefineParameter(2, ParameterAttributes.None, "value");
				writeAll.InitLocals = true;

				var getHierarchy = typeof(ISerializationContext).GetProperty("Hierarchy").GetGetMethod(); Debug.Assert(getHierarchy != null, "ISerializationContext::Hierarchy is missing");
				var getFormat = typeof(ISerializationContext).GetProperty("Format").GetGetMethod(); Debug.Assert(getHierarchy != null, "ISerializationContext::Format is missing");
				var stackPush = typeof(Stack).GetMethod("Push", new[] { typeof(object) }); Debug.Assert(stackPush != null, "Stack::Push is missing");
				var stackPop = typeof(Stack).GetMethod("Pop"); Debug.Assert(stackPop != null, "Stack::Pop is missing");

				var throwIfEof = typeof(JsonReaderExtentions).GetMethod("ThrowIfEndOfStream", new[] { typeof(IJsonReader), typeof(JsonToken) });
				var throwReaderStackImba = typeof(JsonReaderExtentions).GetMethod("ThrowStackImbalance", new[] { typeof(IJsonReader) });
				var throwWriterStackImba = typeof(JsonWriterExtentions).GetMethod("ThrowStackImbalance", new[] { typeof(IJsonWriter) });
				var throwIfMemberIsEmpty = typeof(JsonReaderExtentions).GetMethod("ThrowIfMemberNameIsEmpty", new[] { typeof(IJsonReader), typeof(string) });


				var allReadMethods = typeof(JsonReaderExtentions).GetMethods();
				var readValue = allReadMethods.Where(m => m.Name.StartsWith("Read") && m.Name != "ReadMember" && m.GetParameters().Length == 2 && m.ReturnType == valueT)
					.DefaultIfEmpty(allReadMethods.Where(m => m.IsGenericMethod && m.Name == "ReadValue").Single().MakeGenericMethod(valueT))
					.SingleOrDefault();
				var allWriteMethods = typeof(JsonWriterExtentions).GetMethods();
				var writeValue = allWriteMethods.Where(m => m.Name.StartsWith("Write") && m.Name != "WriteMember" && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType == valueT)
					.DefaultIfEmpty(allWriteMethods.Where(m => m.IsGenericMethod && m.Name == "WriteValue").Single().MakeGenericMethod(valueT))
					.SingleOrDefault();
				var readMemberMethod = allReadMethods.Where(m => m.Name == "ReadMember" && m.GetParameters().Length == 2).Single();
				var writeMemberMethod = allWriteMethods.Where(m => m.Name == "WriteMember" && m.GetParameters().Length == 2).Single();

				{
					var nextToken = typeof(IJsonReader).GetMethod("NextToken", Type.EmptyTypes); Debug.Assert(nextToken != null, "IJsonReader::NextToken is missing");
					var getToken = typeof(IJsonReader).GetProperty("Token").GetGetMethod(); Debug.Assert(getToken != null, "IJsonReader::Token is missing");
					var getContext = typeof(IJsonReader).GetProperty("Context").GetGetMethod(); Debug.Assert(getContext != null, "IJsonReader::Context is missing");

					var dictSig = isDictionaryInterface ? typeof(IDictionary<,>).MakeGenericType(keyT, valueT) : dictionaryType;
					var dictCtr = (isDictionaryInterface ? typeof(Dictionary<,>).MakeGenericType(keyT, valueT) : dictionaryType).GetConstructor(Type.EmptyTypes); Debug.Assert(dictSig != null, dictSig.Name + "::ctr() is missing");
					var dictAdd = dictSig.GetMethod("Add", new[] { keyT, valueT }) ?? (isGeneric ? typeof(IDictionary<,>).MakeGenericType(keyT, valueT).GetMethod("Add", new[] { keyT, valueT }) : typeof(IDictionary).GetMethod("Add"));
					var convertToKey = typeof(TypeConvert).GetMethods().Single(m => m.Name == "Convert" && m.IsGenericMethod).MakeGenericMethod(typeof(string), keyT);

					var il = readAll.GetILGenerator();
					var dictLocal = il.DeclareLocal(dictionaryType);
					var contextLocal = il.DeclareLocal(typeof(Stack));
					var keyLocal = il.DeclareLocal(keyT);
					var valueLocal = il.DeclareLocal(valueT);
					var keyStrLocal = il.DeclareLocal(typeof(string));

					var whileBodyLabel = il.DefineLabel();
					var whileEndLabel = il.DefineLabel();
					var whileConditionLabel = il.DefineLabel();
					var convertAndReturn = il.DefineLabel();

					il.Emit(OpCodes.Nop);
					il.Emit(OpCodes.Newobj, dictCtr); // new dict
					il.Emit(OpCodes.Stloc_0);
					if (!dictionaryType.IsValueType)
					{
						il.Emit(OpCodes.Ldarg_0); // reader
						il.Emit(OpCodes.Callvirt, getContext); // reader.Context
						il.Emit(OpCodes.Callvirt, getHierarchy); // reader.Context.Hierarchy
						il.Emit(OpCodes.Stloc_1);
						il.Emit(OpCodes.Ldloc_1);
						il.Emit(OpCodes.Ldloc_0);
						il.Emit(OpCodes.Callvirt, stackPush); // reader.Context.Hierarchy.Push(dict)
					}
					il.Emit(OpCodes.Br_S, whileConditionLabel);
					il.MarkLabel(whileBodyLabel);
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Ldc_I4, (int)JsonToken.EndOfObject);
					il.Emit(OpCodes.Call, throwIfEof); // ThrowIfEndOfStream(reader, JsonToken.EndOfObject);					
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Ldc_I4_1); // true
					il.Emit(OpCodes.Call, readMemberMethod);
					il.Emit(OpCodes.Stloc_S, keyStrLocal); // keyStr = reader.ReadMember(true);
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Ldloc_S, keyStrLocal);
					il.Emit(OpCodes.Call, throwIfMemberIsEmpty); // ThrowIfMemberNameIsEmpty(reader, keyStr);					
					il.Emit(OpCodes.Ldloc_S, keyStrLocal); // load keyStr
					if (keyT != typeof(string))
					{
						il.Emit(OpCodes.Ldarg_0); // reader
						il.Emit(OpCodes.Callvirt, getContext); // reader.Context
						il.Emit(OpCodes.Callvirt, getFormat); // reader.Context.Format
						il.Emit(OpCodes.Call, convertToKey); // TypeConvert<KeyT>.Convert<string>(keyStr, reader.Context.Format)
					}
					il.Emit(OpCodes.Stloc_2); // key = (KeyT)keyStr;
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Ldc_I4_0); // false
					il.Emit(OpCodes.Call, readValue);
					il.Emit(OpCodes.Stloc_3); // value = reader.ReadValue<ValueT>(false);
					il.Emit(OpCodes.Ldloc_0); // load dict
					if (dictAdd.DeclaringType != dictionaryType)
						il.Emit(OpCodes.Isinst, dictAdd.DeclaringType);
					il.Emit(OpCodes.Ldloc_2); // load key
					il.Emit(OpCodes.Ldloc_3); // load value
					if (dictAdd.IsVirtual && !dictAdd.IsFinal)
						il.Emit(OpCodes.Callvirt, dictAdd); // dict.Add(key, value);				
					else
						il.Emit(OpCodes.Call, dictAdd); // dict.Add(key, value);				
					if (dictAdd.ReturnType != null && dictAdd.ReturnType != typeof(void))
						il.Emit(OpCodes.Pop);
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Ldc_I4, (int)JsonToken.EndOfObject);
					il.Emit(OpCodes.Call, throwIfEof); // ThrowIfEndOfStream(reader, JsonToken.EndOfObject);
					il.MarkLabel(whileConditionLabel);
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Callvirt, nextToken); // reader.NextToken();
					il.Emit(OpCodes.Brfalse_S, whileEndLabel);
					il.Emit(OpCodes.Ldarg_0); // load reader
					il.Emit(OpCodes.Callvirt, getToken); // reader.Token;
					il.Emit(OpCodes.Ldc_I4, (int)JsonToken.EndOfObject);
					il.Emit(OpCodes.Ceq);
					il.Emit(OpCodes.Brfalse_S, whileBodyLabel);
					il.MarkLabel(whileEndLabel);
					if (!dictionaryType.IsValueType)
					{
						il.Emit(OpCodes.Ldloc_1); // load stack
						il.Emit(OpCodes.Callvirt, stackPop);
						il.Emit(OpCodes.Ldloc_0); // load dict
						il.Emit(OpCodes.Ceq); // stack.Pop() == dict
						il.Emit(OpCodes.Brtrue_S, convertAndReturn);
						il.Emit(OpCodes.Ldarg_0); // load reader
						il.Emit(OpCodes.Call, throwReaderStackImba);
					}
					il.MarkLabel(convertAndReturn);
					il.Emit(OpCodes.Ldloc_0); // load dict
					il.Emit(OpCodes.Isinst, dictionaryType);
					il.Emit(OpCodes.Ret);
				}

				{
					var getContext = typeof(IJsonWriter).GetProperty("Context").GetGetMethod(); Debug.Assert(getContext != null, "IJsonWriter::Context is missing");
					var kvType = isGeneric ? typeof(KeyValuePair<,>).MakeGenericType(keyT, valueT) : typeof(DictionaryEntry);
					var enumType = isGeneric ? typeof(IEnumerable<>).MakeGenericType(kvType) : typeof(IEnumerable);
					var getEnumtor = enumType.GetMethod("GetEnumerator"); Debug.Assert(getEnumtor != null);
					var enumtorType = getEnumtor.ReturnType; Debug.Assert(enumtorType != null);
					var enumMoveNext = typeof(IEnumerator).GetMethod("MoveNext"); Debug.Assert(enumMoveNext != null);
					var enumGetCurrent = enumtorType.GetMethod("get_Current"); Debug.Assert(enumGetCurrent != null);
					var getKey = kvType.GetMethod("get_Key"); Debug.Assert(getKey != null);
					var getValue = kvType.GetMethod("get_Value"); Debug.Assert(getValue != null);
					var convertFromKey = typeof(TypeConvert).GetMethods().Single(m => m.Name == "Convert" && m.IsGenericMethod).MakeGenericMethod(getKey.ReturnType, typeof(string));

					var il = writeAll.GetILGenerator();

					var kvLocal = il.DeclareLocal(kvType);
					var enumLocal = il.DeclareLocal(enumtorType);

					var whileBodyLabel = il.DefineLabel();
					var whileConditionLabel = il.DefineLabel();
					var returnLabel = il.DefineLabel();

					il.Emit(OpCodes.Nop);
					//il.Emit(OpCodes.Ldloca, kvLocal);
					//il.Emit(OpCodes.Initobj, kvType);
					//il.Emit(OpCodes.Ldnull);
					//il.Emit(OpCodes.Stloc_1);

					if (!dictionaryType.IsValueType)
					{
						il.Emit(OpCodes.Ldarg_0); // writer
						il.Emit(OpCodes.Callvirt, getContext); // writer.Context
						il.Emit(OpCodes.Callvirt, getHierarchy); // writer.Context.Hierarchy						
						il.Emit(OpCodes.Ldarg_1); // dict
						il.Emit(OpCodes.Callvirt, stackPush); // writer.Context.Hierarchy.Push(dict)
					}
					il.Emit(OpCodes.Ldarg_1); // dict
					if (getEnumtor.DeclaringType != dictionaryType)
						il.Emit(OpCodes.Isinst, getEnumtor.DeclaringType); // (enum as IEnumerable)
					if (getEnumtor.IsVirtual && !getEnumtor.IsFinal)
						il.Emit(OpCodes.Callvirt, getEnumtor); // enum.GetEnumerator()
					else
						il.Emit(OpCodes.Call, getEnumtor); // enum.GetEnumerator()
					il.Emit(OpCodes.Box, enumtorType); // ---
					il.Emit(OpCodes.Stloc_1); // enumerator = enum.GetEnumerator()
					il.Emit(OpCodes.Br_S, whileConditionLabel);
					il.MarkLabel(whileBodyLabel);
					il.Emit(OpCodes.Ldloc_1); // enumerator
					il.Emit(OpCodes.Callvirt, enumGetCurrent); // enumerator.Current
					if (enumGetCurrent.ReturnType != kvType)
					{
						if (enumGetCurrent.ReturnType.IsValueType)
						{
							il.Emit(OpCodes.Box, enumGetCurrent.ReturnType);
							il.Emit(OpCodes.Castclass, kvType); // (KeyValue)enumerator.Current
						}
						else
						{
							il.Emit(OpCodes.Unbox_Any, kvType); // (KeyValue)enumerator.Current
						}
					}
					il.Emit(OpCodes.Stloc_0); // kv = (KeyValue)enumerator.Current;
					il.Emit(OpCodes.Ldarg_0); // writer
					il.Emit(OpCodes.Ldloca, kvLocal); // load kv
					il.Emit(OpCodes.Call, getKey); // kv.Key
					if (keyT != typeof(string))
					{
						il.Emit(OpCodes.Ldarg_0); // writer
						il.Emit(OpCodes.Callvirt, getContext); // writer.Context
						il.Emit(OpCodes.Callvirt, getFormat); // writer.Context.Format
						il.Emit(OpCodes.Call, convertFromKey); // TypeConvert<string>.Convert<KeyT>(kv.Key, writer.Context.Format)
					}
					il.Emit(OpCodes.Call, writeMemberMethod); // WriteMember(writer, (string)kv.Key)
					il.Emit(OpCodes.Ldarg_0); // writer
					il.Emit(OpCodes.Ldloca, kvLocal); // load kv
					il.Emit(OpCodes.Call, getValue); // kv.Value
					if (getValue.ReturnType != valueT)
					{
						il.Emit(OpCodes.Box, getValue.ReturnType);
						if (valueT != typeof(object))
							il.Emit(OpCodes.Castclass, valueT); // (ValueT)(object)kv.Value
					}
					il.Emit(OpCodes.Call, writeValue); // WriteValue(writer, (ValueT)kv.Value)
					il.MarkLabel(whileConditionLabel);
					il.Emit(OpCodes.Ldloc_1); // enumerator
					il.Emit(OpCodes.Callvirt, enumMoveNext);
					il.Emit(OpCodes.Brtrue_S, whileBodyLabel);
					if (!dictionaryType.IsValueType)
					{
						il.Emit(OpCodes.Ldarg_0); // writer
						il.Emit(OpCodes.Callvirt, getContext); // writer.Context
						il.Emit(OpCodes.Callvirt, getHierarchy); // writer.Context.Hierarchy						
						il.Emit(OpCodes.Callvirt, stackPop); // writer.Context.Hierarchy.Pop()
						il.Emit(OpCodes.Ldarg_1); // array
						il.Emit(OpCodes.Ceq); // array
						il.Emit(OpCodes.Brtrue_S, returnLabel);
						il.Emit(OpCodes.Ldarg_0); // load writer
						il.Emit(OpCodes.Call, throwWriterStackImba);
					}
					il.MarkLabel(returnLabel);
					il.Emit(OpCodes.Ret);

				}

				ReadAll = (Func<IJsonReader, DictionaryT>)readAll.CreateDelegate(typeof(Func<IJsonReader, DictionaryT>));
				WriteAll = (Action<IJsonWriter, DictionaryT>)writeAll.CreateDelegate(typeof(Action<IJsonWriter, DictionaryT>));
			}
		}

		private Type m_dictionaryType;
		private Type m_keyType;
		private Type m_valueType;

		public DictionarySerializer(TypeDescription dictionaryType)
		{
			if (dictionaryType == null)
				throw new ArgumentNullException("dictionaryType");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			this.TypeDescription = dictionaryType;

			m_dictionaryType = dictionaryType.GetClrType();
			m_keyType = typeof(string);
			m_valueType = typeof(object);

			if (m_dictionaryType.HasMultipleInstantiations(typeof(IDictionary<,>)))
				throw new Exceptions.TypeContractViolation(this.GetType(), dictionaryType, "have only one generic IDictionary interface");

			if (m_dictionaryType.IsInstantiationOf(typeof(IDictionary<,>)))
			{
				//m_isGenericDictionary = true;
				var genArgs = m_dictionaryType.GetInstantiationArguments(typeof(IDictionary<,>));
				m_keyType = genArgs[0];
				m_valueType = genArgs[1];
			}
			else if (m_dictionaryType == typeof(object))
				m_dictionaryType = Json.DefaultDictionaryType ?? typeof(Hashtable);
			else if (typeof(IDictionary).IsAssignableFrom(m_dictionaryType))
				return; // do nothink
			else
				throw new Exceptions.TypeContractViolation(this.GetType(), dictionaryType, "be dictionary");
		}

		public override DictionaryT Deserialize(IJsonReader reader)
		{
			if (reader == null)
				throw new ArgumentNullException("reader");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			if (reader.Token == JsonToken.Null)
				return default(DictionaryT);

			if (reader.Token != JsonToken.BeginObject)
				throw new Exceptions.UnexpectedToken(reader, JsonToken.BeginObject);

			//var container = new Dictionary<string, object>(10);
			//reader.Context.Hierarchy.Push(container);
			//while (reader.NextToken() && reader.Token != JsonToken.EndOfObject)
			//{
			//    if (reader.Token != JsonToken.Member)
			//        throw new Exceptions.UnexpectedToken(reader, JsonToken.Member);

			//    string key = null;
			//    object value = null;

			//    key = reader.RawValue as string; // string
			//    reader.NextToken();
			//    value = reader.ReadValue(m_valueType, false);

			//    container[key] = value; // overwrite old value
			//}
			//reader.Context.Hierarchy.Pop();

			//return (DictionaryT)PopulateDictionary(container, reader.Context.Format);

			var dictionary = CachedSerializationMethods.ReadAll(reader);
			return dictionary;
		}
		public override void Serialize(IJsonWriter writer, DictionaryT dictionary)
		{
			if (writer == null)
				throw new ArgumentNullException("writer");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			if (dictionary == null)
			{
				writer.WriteNull();
				return;
			}

			if (this.TypeDescription.IsObjectType)
			{
				writer.WriteObjectBegin();
				writer.WriteObjectEnd();
				return;
			}

			//writer.Context.Hierarchy.Push(dictionary);
			//// object
			//writer.Write(JsonToken.BeginObject, null);
			//// serialize dynamic object
			//if (dictionary is IDictionary<string, object>)
			//{
			//    var dict = (dictionary as IDictionary<string, object>);
			//    foreach (var pair in dict)
			//    {	// key
			//        writer.Write(JsonToken.Member, pair.Key);
			//        // value
			//        writer.WriteValue(pair.Value, m_valueType);
			//    }
			//}
			//// serialize basic dictionary
			//else if( m_isGenericDictionary )
			//{
			//    var dict = GenericDictionaryWrap.Create(dictionary, m_keyType, m_valueType);
			//    dict.WriteAll(writer);				
			//}
			//// serialize generic dictionary
			//else if (dictionary is IDictionary)
			//{
			//    var dict = (dictionary as IDictionary);
			//    foreach (DictionaryEntry pair in dict)
			//    {
			//        // key
			//        writer.Write(JsonToken.Member, Convert.ToString(pair.Key, writer.Context.Format));
			//        // value
			//        writer.WriteValue(pair.Value, m_valueType);
			//    }
			//}
			//else
			//    throw new Exceptions.TypeContractViolation(this.GetType(), this.SerializableType, "be dictionary");

			writer.WriteObjectBegin();
			CachedSerializationMethods.WriteAll(writer, dictionary);
			writer.WriteObjectEnd();
		}

		public override string ToString()
		{
			return string.Format("dictionary of {1}:{2}, {0}", m_dictionaryType, m_keyType, m_valueType);
		}
	}

	abstract class GenericDictionaryWrap : IEnumerable<DictionaryEntry>, IEnumerable
	{
		public abstract object WrappedDictionary { get; }
		public abstract Type KeyType { get; }
		public abstract Type ValueType { get; }

		public abstract object this[object key] { get; set; }

		#region IEnumerable<DictionaryEntry> Members

		public abstract IEnumerator<DictionaryEntry> GetEnumerator();


		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}

		#endregion

		/// <summary>
		/// Creates new instance of GenericDictionaryWrap
		/// </summary>
		/// <param name="value">instance of generic dictionary</param>
		/// <returns>new instance of GenericDictionaryWrap</returns>
		/// <exception cref="ArgumentException">value is not generic dictionary</exception>
		/// <exception cref="ArgumentNullException">value is null</exception>
		public static GenericDictionaryWrap Create(object value, Type keyType, Type valueType)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			if (keyType == null)
				throw new ArgumentNullException("keyType");
			if (valueType == null)
				throw new ArgumentNullException("valueType");
			System.Diagnostics.Contracts.Contract.EndContractBlock();

			var wrapType = typeof(GenericDictionaryWrap<,,>).MakeGenericType(value.GetType(), keyType, valueType);
			return (GenericDictionaryWrap)Activator.CreateInstance(wrapType, value);
		}


		public abstract void WriteAll(IJsonWriter writer);
	}
	sealed class GenericDictionaryWrap<ItemT, KeyT, ValueT> : GenericDictionaryWrap where ItemT : IDictionary<KeyT, ValueT>
	{
		private IDictionary<KeyT, ValueT> m_internalDict;

		public override Type KeyType { get { return typeof(KeyT); } }
		public override Type ValueType { get { return typeof(ValueT); } }
		public override object WrappedDictionary { get { return m_internalDict; } }

		public GenericDictionaryWrap(IDictionary<KeyT, ValueT> dict)
		{
			m_internalDict = dict;
		}
		public override object this[object key]
		{
			get
			{
				if (key == null)
					throw new ArgumentNullException("key");
				System.Diagnostics.Contracts.Contract.EndContractBlock();

				return m_internalDict[(KeyT)key];
			}
			set
			{
				if (key == null)
					throw new ArgumentNullException("key");
				System.Diagnostics.Contracts.Contract.EndContractBlock();

				m_internalDict[(KeyT)key] = (ValueT)value;
			}
		}

		public override void WriteAll(IJsonWriter writer)
		{
			foreach (var kv in m_internalDict)
			{
				// key
				writer.Write((JsonMember)TypeConvert.Convert<KeyT, string>(kv.Key, writer.Context.Format));
				// value
				writer.WriteValue(kv.Value);
			}
		}

		public override IEnumerator<DictionaryEntry> GetEnumerator()
		{
			foreach (var pair in m_internalDict)
			{
				yield return new DictionaryEntry(pair.Key, pair.Value);
			}
		}
	}
}
